package com.bindingfly.client.formrender;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;

import com.bindingfly.client.formrender.factory.WidgetFactor;
import com.bindingfly.client.formrender.widget.types.InjectableContainer;
import com.bindingfly.client.formrender.widget.types.InjectableWidget;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * This widget will render a request form and inject the widget end needed.
 * 
 * @author malaskat
 */
public class RequestRendererWidget extends HTMLPanel
{
	final private String	  rootElementId;
	HashMap<String, WidgetInjecterPojo>	  renderedWidgetMap	= new HashMap<String,WidgetInjecterPojo>();
	
	
	
	public RequestRendererWidget(String id)
	{
		super("<span id=\"" + id + "\" widgetContainerType=\"panel\"></span>");
		rootElementId = id;
	}

	public void defineRequestDefinition(String definition)
	{
		defineRequestDefinition(definition, null);
	}

	
	/**
	 * This will set the defintion of the request which we show the request to the user
	 * 
	 * @param definition
	 */
	public void defineRequestDefinition(String definition, String values)
	{
		Element rootElement = DOM.getElementById(rootElementId);
		rootElement.setInnerHTML(definition);
		renderedWidgetMap.clear();
		
		Date startDate = new Date();
		
		populateRootWidgetContainer(rootElement);
		
		System.out.println("Finished populateRootWidgetContainer " +((new Date()).getTime() - startDate.getTime() ));
		
		runPostProcessingActivities();
		
		System.out.println("Finished post processing " +((new Date()).getTime() - startDate.getTime() ));
		
		//populateValues(values);
		
		System.out.println("Form Render" +  "Finished post processing " +((new Date()).getTime() - startDate.getTime() ));
		
	}

	private void runPostProcessingActivities()
	{
		Collection<WidgetInjecterPojo> containers = renderedWidgetMap.values();
		for (WidgetInjecterPojo container: containers)
		{
			if (container.injectableWidget.doesRequirePostProcessExecution())
			{
				container.injectableWidget.executePostProcess(container.widget, renderedWidgetMap);
			}
		}
	} 
	
	/**
	 * This is the first method in a recursive function that will create all the 
	 * container in the request definition.
	 * @param rootElement
	 */
	private void populateRootWidgetContainer(Element rootElement)
	{
		tryToMakeWidgetContainerFromElement(null, rootElement);
	}

	
	/** 
	 * This will check to see if the element is a to be converted into a container widget.
	 * 
	 * @param htmlPanel
	 * @param possibleWidget
	 */
	private void tryToMakeWidgetContainerFromElement(HTMLPanel htmlPanel, Element possibleWidget)
	{
		if (possibleWidget != null)
		{
			String existDomId = possibleWidget.getId();
			if (possibleWidget.hasAttribute("widgetContainerType") && renderedWidgetMap.containsKey(existDomId) == false)
			{
				System.out.println("    M: Creating widget container: " + existDomId);

				WidgetInjecterPojo widgetInjecterPojo = (WidgetInjecterPojo) WidgetFactor.generateWidgetContainer(possibleWidget);

				//If there is a html panel then use it.  If not use the rootpanel to find the element
				if (htmlPanel == null)
				{
					putWidgetInRootElementById(existDomId, widgetInjecterPojo.widget);
				} else
				{
					htmlPanel.addAndReplaceElement(widgetInjecterPojo.widget, (com.google.gwt.user.client.Element) possibleWidget);
				}

				//update the new widget id with the element id that you replaced
				widgetInjecterPojo.widget.getElement().setAttribute("id", existDomId);
				
				//add the id to the set of ids not to reprocess
				renderedWidgetMap.put(existDomId, widgetInjecterPojo);

				HTMLPanel newWidgetHtmlPanel = (HTMLPanel) ((SimplePanel)widgetInjecterPojo.widget).getWidget();

				//populate all child containers
				populateWidgetContainers(existDomId, newWidgetHtmlPanel);
				//populate all child widget
				//TODO why did I only allow children when an instanceof DetailedDataContentPanel
				//boolean allowChildWidgetsInGlobalMap = newContainerWidget instanceof DetailedDataContentPanel;
				boolean allowChildWidgetsInGlobalMap  = true;
				 
				//TODO need to add panels in
				//System.out.println("allowChildWidgetsInGlobalMap: " + allowChildWidgetsInGlobalMap + " " + newContainerWidget + " " + (newContainerWidget instanceof DetailedDataContentPanel));
				
				HashMap<String, WidgetInjecterPojo> directChildrenMap = populateWidgets(existDomId, newWidgetHtmlPanel, allowChildWidgetsInGlobalMap);
				
				InjectableContainer injectableContainer = ((InjectableContainer)widgetInjecterPojo.injectableWidget);
				
				if (injectableContainer.needKnowledgeOfChildren())
				{
					injectableContainer.populateChildrenWidgets(widgetInjecterPojo.widget, directChildrenMap);
				}
			}else
			{
				System.out.println("    X: Not Creating Container for -" + possibleWidget.getId());
				System.out.println("        possibleWidget.hasAttribute(\"widgetContainerType\") -" + possibleWidget.hasAttribute("widgetContainerType"));
				System.out.println("        renderedWidgetSet.contains(possibleWidget.getId()) -" + renderedWidgetMap.containsKey(possibleWidget.getId()));
			}
		} else
		{
			System.out.println("    X: Not Creating Container for - it was null");
		}
	}

	private void putWidgetInRootElementById(String existDomId, Widget widget)
	{
		try
		{
			RootPanel rootPanel = RootPanel.get(existDomId);
			rootPanel.add(widget);
		} catch (AssertionError t) // TODO: This is really weird on first try this will fail but in failing it fixes the
									// problem. Needs more research.
		{
			RootPanel rootPanel = RootPanel.get(existDomId);
			rootPanel.add(widget);
		}
	}

	private HashMap<String, WidgetInjecterPojo> populateWidgets(String id, HTMLPanel htmlPanel,  boolean allowWidgetToBeInGlobalMap)
	{
		System.out.println("populating Widgets ---------------------------" + id);
		
		HashMap<String, WidgetInjecterPojo> createdWidgets = new HashMap<String, WidgetInjecterPojo>();
		NodeList<Element> nodeList = null;
		nodeList = getAllChildrenSpanNodes(id);
		int lengthOfNodes = nodeList.getLength();

		System.out.println("    C: There are " + lengthOfNodes + " in node " + id);

		for (int i = lengthOfNodes - 1; i > -1; i--)
		{
			Node node = nodeList.getItem(i);
			Element possibleWidget = Element.as(node);

			String widgetId = possibleWidget.getId();
			
			if (possibleWidget.hasAttribute("widgetType") && renderedWidgetMap.containsKey(widgetId) == false)
			{
				System.out.println("    M: Creating widget: " + widgetId);
				WidgetInjecterPojo widgetInjecterPojo = WidgetFactor.generateWidget(possibleWidget, false);
				
				htmlPanel.addAndReplaceElement(widgetInjecterPojo.widget, (com.google.gwt.user.client.Element) possibleWidget);
				System.out.println("        Widget Created.");
				
				createdWidgets.put(widgetId, widgetInjecterPojo);
				renderedWidgetMap.put(widgetId,widgetInjecterPojo );
				
			} else
			{
				System.out.println("    X: Not Creating Widget for-" + widgetId);
				System.out.println("       possibleWidget.hasAttribute(\"widgetType\")-" + possibleWidget.hasAttribute("widgetType"));
				System.out.println("       renderedWidgetSet.contains(possibleWidget.getId())-" + renderedWidgetMap.containsKey(possibleWidget.getId()));
			}
		}
		System.out.println("Finished Widgets  -------------------" + id);
		return createdWidgets;
	}

	private void populateWidgetContainers(String id, HTMLPanel htmlPanel)
	{

		System.out.println("populating Widgets Containers -------------------" + id);
		NodeList<Element> nodeList = null;
		nodeList = getAllChildrenSpanNodes(id);

		int lengthOfNodes = nodeList.getLength();
		for (int i = 0; i < lengthOfNodes; i++)
		{
			Node node = nodeList.getItem(i);
			
			try
			{
				if (node != null)
				{
					Element possibleWidget = Element.as(node);
					tryToMakeWidgetContainerFromElement(htmlPanel, possibleWidget);
				}
			} catch (Throwable e)
			{
				System.out.println("!!! Not a Element : " + node + " parent: " + node.getParentElement());
			}
		}
		System.out.println("Finished Widgets Containers -------------------" + id);
	}

	private NodeList<Element> getAllChildrenSpanNodes(String id)
	{
		NodeList<Element> nodeList;
		if (id != null)
		{
			nodeList = DOM.getElementById(id).getElementsByTagName("span");
		} else
		{
			nodeList = RootPanel.getBodyElement().getElementsByTagName("span");
		}
		return nodeList;
	}
	
	

}
